package connect4client.net.rmi;

import java.io.IOException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

import connect4client.net.servers.*;
import rmiserver.ClientRemote;
import rmiserver.LogonService;
import rmiserver.ServerRemote;
import connect4client.core.Controller;
import connect4client.core.MessageManager;
import messages.LoginMessage;
import messages.Message;
import messages.RegisterMessage;
import connect4client.net.ConnectionController;


public class ConnectionControllerRemote extends ConnectionController  {
		
	public ConnectionControllerRemote(Controller controller, Server server) throws IOException {
		super(controller, server);
		
		remoteSender = new RemoteSender();
		new Thread(this, this.getClass().getCanonicalName()).start();
	}
	
	
	public void messageReceived(Message message) {
		controller.getMessageConsumer().putMessage(message);
	}
	
	
	@Override
	public void sendMessage(Message message) {
		try {
			this.remoteSender.sendMessage(message);
		} catch (RemoteException e) {
			System.err.println("Failed to deliver message " + message);
			e.printStackTrace();
			if (!reconnecting)
				reconnect();
		}
	}
	
	
	@Override
	public void sendMessageOrFail(Message message) throws IOException {
		sendMessage(message);
	}
	
	
	@Override
	public void login(String login, String password) {	
		LoginMessage message = new LoginMessage(login, password);
		message.setId(-1);
		message.setLastReceivedId(-1);
		controller.getMessageManager().incrementMessageCounter();
		
		try {
			loginMutex.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.remoteSender.login(message);
		loginMutex.release();
	}
	
	
	@Override
	public void register(String login, String password) {	
		RegisterMessage message = new RegisterMessage(login, password);
		message.setId(-1);
		message.setLastReceivedId(-1);
		controller.getMessageManager().incrementMessageCounter();
		
		try {
			loginMutex.acquire();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.remoteSender.register(message);
		loginMutex.release();
	}


	public void run() {
		// Do nothing.
	}

	
	@Override
	public boolean retry() {
		try {
			logonService = (LogonService) Naming.lookup(ServerRMIString());
		} catch (Exception e) {
			return false;
		}
		
		return true;
	}
		
	
	@Override
	public boolean reAuthenticate() {
		LoginMessage loginMessage = new LoginMessage(controller.getUser().getLogin(), controller.getUser().getPassword(),true);					
		MessageManager messageManager = controller.getMessageManager();
		
		synchronized (messageManager) {
			loginMessage.setId(-1);
			loginMessage.setLastReceivedId(messageManager.getLastReceivedId());
		}
		
		return remoteSender.reAuthenticate(logonService, loginMessage);
	}
		
	
	private String ServerRMIString() {
		return "rmi://" + runningServer.getIp().getHostAddress() + ":" + runningServer.getPort() + "/" + "LogonService";
	}
	
	
	private RemoteSender remoteSender;
	private LogonService logonService;
	
	public class RemoteSender extends UnicastRemoteObject implements ClientRemote {
		
		public RemoteSender() throws RemoteException {		
			super();
		}
		
		
		/**
		 * This method is accessed by the server via RMI
		 * 
		 * @param message  The message sent by the Server
		 */
		public void send(Message message) throws RemoteException {	
			ConnectionControllerRemote.this.messageReceived(message);			
		}
		
		
		public void login(LoginMessage message) {
			System.err.println("Trying to login");
			LogonService logonService;
			try {
				logonService = (LogonService) Naming.lookup(ServerRMIString());
				this.serverRemote = logonService.login(message, this);
				System.err.println("logged in");
			} catch (Exception e) {
				System.err.println("Login failed: " + e);
				e.printStackTrace();
			} 
		}
		
		
		public void register(RegisterMessage message) {
			System.out.println("Trying to register");
			LogonService logonService;
			try {
				logonService = (LogonService) Naming.lookup(ServerRMIString());
				this.serverRemote = logonService.register(message, this);
			} catch (Exception e) {
				System.err.println("Register failed: " + e);
			} 
		}
		

		/**
		 * This method is used to send messages to the server.
		 * 
		 * @para message  The message to be send to the Server
		 */
		public void sendMessage(Message message) throws RemoteException {				
			this.serverRemote.send(message);					
		}
		

		public boolean reAuthenticate(LogonService logonService, LoginMessage loginMessage) {			
			try {
				this.serverRemote = logonService.login(loginMessage,this);
				System.err.println("logado com sucesso!");
			} catch (RemoteException e) {
				return false;
			}		
			
			return true;
		} 
				
			
		private ServerRemote serverRemote = null;
		
		private static final long serialVersionUID = 1L;
	}
}
